#include <openssl/evp.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <stdexcept>

class FileToMD5{
public:
    static std::string md5_string(const std::string& binary_data) {
        EVP_MD_CTX* ctx = EVP_MD_CTX_new();
        const EVP_MD* md = EVP_md5();
    
        unsigned char digest[EVP_MAX_MD_SIZE];
        unsigned int digest_len = 0;
    
        EVP_DigestInit_ex(ctx, md, nullptr);
        EVP_DigestUpdate(ctx, binary_data.data(), binary_data.size());
        EVP_DigestFinal_ex(ctx, digest, &digest_len);
        EVP_MD_CTX_free(ctx);
    
        std::ostringstream oss;
        for (unsigned int i = 0; i < digest_len; ++i)
            oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(digest[i]);
    
        return oss.str();
    }
    
    static std::string md5_file(const std::string& filename) {
        const int bufSize = 4096;
        unsigned char buffer[bufSize];
    
        EVP_MD_CTX* ctx = EVP_MD_CTX_new();
        if (!ctx) throw std::runtime_error("Failed to create EVP_MD_CTX");
    
        const EVP_MD* md = EVP_md5();
        unsigned char digest[EVP_MAX_MD_SIZE];
        unsigned int digest_len = 0;
    
        if (!EVP_DigestInit_ex(ctx, md, nullptr))
            throw std::runtime_error("EVP_DigestInit_ex failed");
    
        std::ifstream file(filename, std::ios::binary);
        if (!file) throw std::runtime_error("Cannot open file: " + filename);
    
        while (file.good()) {
            file.read(reinterpret_cast<char*>(buffer), bufSize);
            std::streamsize bytesRead = file.gcount();
            if (bytesRead > 0) {
                if (!EVP_DigestUpdate(ctx, buffer, bytesRead))
                    throw std::runtime_error("EVP_DigestUpdate failed");
            }
        }
    
        if (!EVP_DigestFinal_ex(ctx, digest, &digest_len))
            throw std::runtime_error("EVP_DigestFinal_ex failed");
    
        EVP_MD_CTX_free(ctx);
    
        std::ostringstream oss;
        for (unsigned int i = 0; i < digest_len; ++i)
            oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(digest[i]);
    
        return oss.str();
    }
    
};

// int main() {
//     std::string filename = "/home/lyt/OnlineJudge/oj-frontend/src/assets/image/male.jpg"; // 替换为你的文件路径
//     try {
//         std::string hash = md5_file(filename);
//         std::cout << "MD5(" << filename << ") = " << hash << std::endl;
//     } catch (const std::exception& ex) {
//         std::cerr << "Error: " << ex.what() << std::endl;
//     }
//     // 5304dd02c0b35bc789ef47c3feee5a57
//     return 0;
// }
